package com.cmcc.sdtp.sdtp.message;

import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.Date;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.cmcc.sdtp.sdtp.util.MessageUtil;
import com.cmcc.xdr.util.JudgeBytes;
import com.google.common.hash.HashCode;

/**
 * 
 * @author Administrator
 * 
 */
public class SDTPMessage {

	private static Logger logger = LogManager.getLogger(SDTPMessage.class);
	/**
	 * SDTP协议消息头长度 *
	 */
	static final int HEADER_LENGTH = 9;

	/**
	 * 2 Unsigned Integer 消息总长度(含消息头及消息体)
	 */
	private int totalLength;

	/**
	 * 2 Unsigned Integer 消息类型
	 */
	private MessageType messageType;

	/**
	 * 4 Unsigned Integer 交互的流水号，顺序累加，步长为1，循环使用（一个交互的一对请求和应答消息的流水号必须相同）
	 */
	private int sequenceId;

	/**
	 * 1 Unsigned Integer 消息体中的事件数量（最多40条） 若考虑实时性要求，可每次只填一个事件
	 */
	private int totalContents;

	private MessageBody msgBody;

	private ByteBuffer buffer;

	public SDTPMessage() {
	}

	/**
	 * 根据字节数据构造消息 <br>
	 * TODO 这种方式生成的类buffer中不包含totalLength的字节数据，如后期需要修改要修改这个方法
	 * 
	 * @param totalLength
	 *            消息总长度
	 * @param data
	 *            消息缓冲区数据(不包括totalLength)
	 */
	public static SDTPMessage decode(int totalLength, ByteBuffer buffer) {
		SDTPMessage message = new SDTPMessage();
		
		buffer.flip();
		message.totalLength = totalLength;
		message.buffer = buffer;
		// 消息类型
		int iMessageType = message.loadInt(2);
//		logger.error("iMessageType:"+iMessageType);
		message.messageType = MessageType.getEnum(iMessageType);
//		logger.error("message.messageType:"+message.messageType);
		// 流水号
		message.sequenceId = message.loadSequenceId(4);
//		logger.error("message.sequenceId:"+message.sequenceId);
		// 事件数量
		message.totalContents = message.loadInt(1);
//		logger.error("message.totalContents:"+message.totalContents);
		message.decodeMsgBody();
		
		return message;
		
	}

	/**
	 * 根据消息类型解析消息体
	 */
	private void decodeMsgBody() {
		logger.info("messageType:" + messageType.getDesc());
		switch (messageType) {
		case verNego_Req:
			logger.debug("verNego_Req");
			msgBody = new VerNegoReqMsg(this);
			break;
		case verNego_Resp:
			logger.debug("verNego_Resp");
			msgBody = new VerNegoRespMsg(this);
			break;
		case linkAuth_Req:
			logger.debug("linkAuth_Req");
			msgBody = new LinkAuthReqMsg(this);
			break;
		case linkAuth_Resp:
			logger.debug("linkAuth_Resp");
			msgBody = new LinkAuthRespMsg(this);
			break;
		case linkCheck_Req:
			logger.debug("linkCheck_Req");
			msgBody = new LinkCheckReqMsg(this);
			break;
		case linkCheck_Resp:
			logger.debug("linkCheck_Resp");
			msgBody = new LinkCheckRespMsg(this);
			break;
		case XDRRawDataSend_Req:
			logger.debug("XDRRawDataSend_Req");
			msgBody = new XDRRawDataSendReqMsg(this);
			break;
		case XDRRawDataSend_Resp:
			logger.debug("XDRRawDataSend_Resp");
			msgBody = new XDRRawDataSendRespMsg(this);
			break;
		case notifyXDRData_Req:
			logger.debug("notifyXDRData_Req");
			msgBody = new XDRDataNotifyReqMsg(this);//我们需要的数据解析在这
			break;
		case notifyXDRData_Resp:
			logger.debug("notifyXDRData_Resp");
			msgBody = new XDRDataNotifyRespMsg(this);
			break;
		case linkDataCheck_Req:
			logger.debug("linkDataCheck_Req");
			msgBody = new LinkDataCheckReqMsg(this);
			break;
		case linkDataCheck_Resp:
			logger.debug("linkDataCheck_Resp");
			msgBody = new LinkDataCheckRespMsg(this);
			break;
		case linkRel_Req:
			logger.debug("linkRel_Req");
			msgBody = new LinkRelReqMsg(this);
			break;
		case linkRel_Resp:
			logger.debug("linkRel_Resp");
			msgBody = new LinkRelRespMsg(this);
			break;
		default:
			logger.error("错误的消息类型 "+new IllegalArgumentException("错误的消息类型"));
			throw new IllegalArgumentException("错误的消息类型");
		}
	}

	/**
	 * 根据消息内容构造消息和字节数据
	 * 
	 * @param messageType
	 * @param sequenceId
	 * @param totalContents
	 * @param messageBody
	 * @return
	 */
	public void encodeMessage() {
		this.totalLength = HEADER_LENGTH + msgBody.getLength();
		this.totalContents = 1;

		buffer = ByteBuffer.allocate(totalLength);
		write(totalLength, 2);
		write(messageType.getValue(), 2);
		write(sequenceId, 4);
		write(totalContents, 1);
		msgBody.encode();
		buffer.flip();
	}

	// 从byteBuffer中读数据
	// TODO 边界检查

	/**
	 * 从buff中读取指定长度的数据,buffer的pos会改变所以需要按顺序读取
	 * 
	 * @param length
	 * @return
	 */
	public byte[] loadByte(int length) {
		byte[] temp = new byte[length];
		buffer.get(temp);
		return temp;
	}

	/**
	 * @comment 从buff中读取指定长度的数据被将其转换为ip地址。ipv4仅占用四个字节所以将多余的截去。
	 * 
	 * @param length
	 * @return
	 */
	public String loadIp(int length) {
		byte[] b = loadByte(length);
		if(JudgeBytes.judgeBytesIfFdefault(b, length)){//如果這個值等於length个F，则是默认值，取"";
			return "";
		};
		return MessageUtil.toIpAddress(b);
	}

	

	/**
	 * 从buff中读取指定长度的数据并转换为BigInteger
	 * 
	 * @param length
	 * @return
	 */
	private BigInteger loadBigInteger(int length) {
		byte[] b = loadByte(length);
		if(JudgeBytes.judgeBytesIfFdefault(b, length)){//如果這個值等於length个F，则是默认值，取"";
			return new BigInteger("0");
		};
		return MessageUtil.toBigInteger(b);
	}

	
	/**
	 * 从buff中读取指定长度的数据并转换为long buffer的pos会改变所以需要按顺序读取
	 * 
	 * @param length
	 * @return
	 */
	public long loadLong(int length) {
		byte[] b = loadByte(length);
		if(JudgeBytes.judgeBytesIfFdefault(b, length)){//如果這個值等於length个F，则是默认值，取"";
			return 0;
		};
		return MessageUtil.toBigInteger(b).longValue();

	}

	/**
	 * 从buff中读取指定长度的数据并转换为int buffer的pos会改变所以需要按顺序读取
	 * 
	 * @param length
	 * @return
	 */
	public int loadInt(int length) {
		byte[] b = loadByte(length);
		if(JudgeBytes.judgeBytesIfFdefault(b, length)){//如果這個值等於length个F，则是默认值，取"";
			return 0;
		};
		int val = MessageUtil.toBigInteger(b).intValue();
		if(val > 2147483648l || val < 0){//如果大于int的最大值则存0
			return 0;
		}
		return val;
//		return MessageUtil.toBigInteger(loadByte(length)).intValue();
	}
	
	/**
	 * 从buff中读取指定长度的数据并转换为int buffer的pos会改变所以需要按顺序读取
	 * 读取SequenceId 专用
	 * @param length
	 * @return
	 */
	public int loadSequenceId(int length) {
		byte[] b = loadByte(length);
		if(JudgeBytes.judgeBytesIfFdefault(b, length)){//如果這個值等於length个F，则是默认值，取"";
			return 0;
		};
		int val = MessageUtil.toBigInteger(b).intValue();
		return val;
	}
	
	/**
	 * 从buff中读取指定长度的数据并转换为int buffer的pos会改变所以需要按顺序读取,返回的是字符串
	 * 
	 * @param length
	 * @return
	 */
	public String loadStrInt(int length) {
		byte[] b = loadByte(length);
		if(JudgeBytes.judgeBytesIfFdefault(b, length)){//如果這個值等於length个F，则是默认值，取"";
			return "";
		};
		return MessageUtil.toBigInteger(b).intValue()+"";
	}
	
	public String loadStrLong(int length) {
		byte[] b = loadByte(length);
		if(JudgeBytes.judgeBytesIfFdefault(b, length)){//如果這個值等於length个F，则是默认值，取"";
			return "";
		};
		return MessageUtil.toBigInteger(b).longValue() + "";

	}
	
	public String loadStrLongstr(int length) {//数据库里存的是int
		byte[] b = loadByte(length);
		if(JudgeBytes.judgeBytesIfFdefault(b, length)){//如果這個值等於length个F，则是默认值，取"0";
			return "0";
		};
		long val = MessageUtil.toBigInteger(b).longValue();
		if(val > 2147483648l){//如果大于int的最大值则存0
			return "0";
		}
		return val + "";

	}
	
	/**
	 * 从buff中读取指定长度的数据并转换为int buffer的pos会改变所以需要按顺序读取，如果参数带有0，默认值则返回-1
	 * 
	 * @param length
	 * @return
	 */
	public int loadInt2(int length) {
		byte[] b = loadByte(length);
		if(JudgeBytes.judgeBytesIfFdefault(b, length)){//如果這個值等於length个F，则是默认值，取-1;
			return -1;
		};
		return MessageUtil.toBigInteger(b).intValue();
//		return MessageUtil.toBigInteger(loadByte(length)).intValue();
	}

	/**
	 * 从buff中读取指定长度的数据并转换为string buffer的pos会改变所以需要按顺序读取
	 * 
	 * @param length
	 * @return
	 */
	public String loadStr(int length) {
		byte[] b = loadByte(length);
		if(JudgeBytes.judgeBytesIfFdefault(b, length)){//如果這個值等於length个F，则是默认值，取"";
			return "";
		};
		for (int i = 0; i < b.length; i++) {
			byte temp = b[i];
			if(temp == -1){//如果是F补位则总长度减1
				length --;
			}
		}
		if(b.length > length){//如果长度有减则是由F补位，则重新定一个byte[]然后解析成字符串，防止乱码
			byte[] bytelast = new byte[length];
			bytelast = subBytes(b,0,length);
			return new String(bytelast);
		}
		return new String(b);
//		return new String(loadByte(length));

	}

	 /**
	  * 从一个byte[]数组中截取一部分
	  * @param src
	  * @param begin
	  * @param count
	  * @return
	  */
	 public static byte[] subBytes(byte[] src, int begin, int count) {
	     byte[] bs = new byte[count];
	     for (int i=begin; i<begin+count; i++) bs[i-begin] = src[i];
	     return bs;
	 }
	

	/**
	 * 从buff中读取指定长度的数据并转换为16进制Hash str, buffer的pos会改变所以需要按顺序读取
	 * 
	 * @param length
	 * @return
	 */
	public String loadHexStr(int length) {
		byte[] b = loadByte(length);
		if(JudgeBytes.judgeBytesIfFdefault(b, length)){//如果這個值等於length个F，则是默认值，取"";
			return "";
		};
		return HashCode.fromBytes(b).toString();
	}
	
	/**
	 * 根据byte数据并转换为16进制Hash str
	 * 
	 * @param length
	 * @return
	 */
	public String loadHexStrByByte(byte[] byteData) {
		return HashCode.fromBytes(byteData).toString();
	}


	// TODO load(data,length)系方法存在参数传递问题
	// Note that java.lang.Integer is immutable.
	// try using org.apache.commons.lang.mutable.MutableInt from Apache Commons
	// library.

	/**
	 * 从buff中读取指定长度的数据,buffer的pos会改变所以需要按顺序读取
	 * 
	 * @param length
	 * @return
	 */
	public byte[] load(byte[] data, int length) {
		data = loadByte(length);
		return data;
	}

	/**
	 * 从buff中读取指定长度的数据并转换为long.buffer的pos会改变所以需要按顺序读取
	 * 
	 * @param length
	 * @return
	 */
	public Long load(Long data, int length) {
		data = loadBigInteger(length).longValue();
		return data;
	}

	/**
	 * 从buff中读取指定长度的数据并转换为Date.buffer的pos会改变所以需要按顺序读取
	 * 
	 * @param length
	 * @return
	 */
	public Date load(Date data, int length) {
		long milliseconds = loadBigInteger(length).longValue();
		data = new Date(milliseconds);
		return data;
	}

	/**
	 * 从buff中读取指定长度的数据并转换为int.buffer的pos会改变所以需要按顺序读取
	 * 
	 * @param length
	 * @return
	 */
	public int load(Integer data, int length) {
		data = loadBigInteger(length).intValue();
		return data;
	}
	
	/**
	 * 将byte解析为int
	 * 原 load方法经过byte直接解析成数字int
	 * @param length
	 * @return
	 */
	public int loadIntByByte(Integer data, byte[] byteData) {
		data = MessageUtil.toBigInteger(byteData).intValue();
		return data;
	}

	/**
	 * 从buff中读取指定长度的数据并转换为string buffer的pos会改变所以需要按顺序读取
	 * 
	 * @param length
	 * @return
	 */
	public String load(String data, int length) {
		data = new String(loadByte(length));
		return data;

	}

	public Double load(Double data, int length) {
		byte[] temp = loadByte(length);
		data = MessageUtil.toDouble(temp);
		return data;
	}

	
	
	// 向bytebuffer中写数据
	public SDTPMessage write(long data, int length) {
		byte[] arr = MessageUtil.toByteArray(data, length);
		return write(arr, length);
	}

	public SDTPMessage write(Date data, int length) {
		return write(data.getTime(), length);
	}

	public SDTPMessage write(String data, int length) {
		byte[] arr = MessageUtil.toByteArray(data, length);
		return write(arr, length);
	}

	public SDTPMessage write(byte[] data, int length) {
		if (data == null) {
			data = MessageUtil.fillByteArray(length, MessageUtil.DEFAULT_BYTE);
		}
		buffer.put(data);
		return this;
	}

	public SDTPMessage write(double data, int length) {
		byte[] arr = MessageUtil.toByteArray(data, length);
		return write(arr, length);
	}

	public ByteBuffer getBuffer() {
		return buffer;
	}

	public int getTotalLength() {
		return totalLength;
	}

	public int getSequenceId() {
		return sequenceId;
	}

	public int getTotalContents() {
		return totalContents;
	}

	public MessageBody getMsgBody() {
		return msgBody;
	}

	public MessageType getMessageType() {
		return messageType;
	}

	public void setTotalLength(int totalLength) {
		this.totalLength = totalLength;
	}

	public void setMessageType(MessageType messageType) {
		this.messageType = messageType;
	}

	public void setSequenceId(int sequenceId) {
		this.sequenceId = sequenceId;
	}

	public void setTotalContents(int totalContents) {
		this.totalContents = totalContents;
	}

	public void setMsgBody(MessageBody msgBody) {
		this.msgBody = msgBody;
	}

	public void setBuffer(ByteBuffer buffer) {
		this.buffer = buffer;
	}

	@Override
	public String toString() {
		return "SDTPMessage [totalLength=" + totalLength + ", messageType=" + messageType + ", sequenceId="
				+ sequenceId + ", totalContents=" + totalContents + "]\n" + "msgBody="
				+ msgBody;
	}

}
